﻿using CLM.Gestao.Administrador.BLL;
using CLM.Gestao.Administrador.DAL;
using CLM.Gestao.Importacao;
using CLM.Gestao.Interfaces;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace CLM.Gestao.Vendas.BLL
{
    public class CustoUnitario : CustoUnitarioBLO<TB_PRODUTO_CUSTO_UNITARIO>
    {
        #region Propriedades

        public int IdProdutoCustoUnitario { get; set; }
        public int IdProduto { get; set; }
        public decimal ValorCustoUnitario { get; set; }

        #endregion Propriedades

        #region Conversões

        private List<TB_PRODUTO_CUSTO_UNITARIO> ConvertToEDM(List<Lista> pLista)
        {
            List<TB_PRODUTO_CUSTO_UNITARIO> List = (from l in pLista
                                                    select new TB_PRODUTO_CUSTO_UNITARIO
                                                    {
                                                        ID_PRODUTO = l.IdProduto,
                                                        ID_PRODUTO_CUSTO_UNITARIO = l.IdProdutoCustoUnitario,
                                                        VALOR_CUSTO_UNITARIO = l.ValorCustoUnitario
                                                    }).ToList();
            return List;
        }

        private List<Lista> ConvertToContext(IQueryable<TB_PRODUTO_CUSTO_UNITARIO> pContext)
        {
            List<Lista> List = (from l in pContext
                                select new Lista
                                {
                                    CodigoProduto = l.TB_PRODUTO.CODIGO_PRODUTO,
                                    IdProduto = l.ID_PRODUTO,
                                    IdProdutoCustoUnitario = l.ID_PRODUTO_CUSTO_UNITARIO,
                                    ValorCustoUnitario = l.VALOR_CUSTO_UNITARIO
                                }).ToList();
            return List;
        }

        #endregion Conversões

        #region CRUD
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pLista"></param>
        public void Add(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Add(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pLista"></param>
        public void Delete(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Delete(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pLista"></param>
        public void Update(List<Lista> pLista)
        {
            try
            {
                foreach (var item in ConvertToEDM(pLista))
                    base.Update(item);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// Efetua a pesquisa na tabela, filtrando as solicitações do usuário
        /// </summary>
        /// <param name="pPesquisa">pPesquisa</param>
        /// <returns>Lista</returns>
        private IQueryable<TB_PRODUTO_CUSTO_UNITARIO> Get(Lista pPesquisa)
        {
            IQueryable<TB_PRODUTO_CUSTO_UNITARIO> Consulta;

            Consulta = from p in GetAll()
                       select p;
            if (pPesquisa != null)
            {
                Consulta = from p in Consulta select p;
            }
            return Consulta;
        }

        #endregion CRUD

        #region Retorna Listas e Itens

        public List<Lista> GetLista()
        {
            return GetLista(new Lista());
        }

        public List<Lista> GetLista(Lista pPesquisa)
        {
            var ListaCustoUnitario = ConvertToContext(Get(pPesquisa));
            return ListaCustoUnitario;
        }

        #endregion Retorna Listas e Itens

        #region Consultas


        #endregion Consultas

        #region Processos

        #region Importa Custo Unitário

        private DataSet dsCustoUnitario;
        private Importacao.Excel ExcelConnection;

        public void ImportaCustoUnitarioProdutos(string pFileName)
        {
            ExcelConnection = new Importacao.Excel();
            dsCustoUnitario = null;

            ValidaLayoutImportacao(pFileName);
            ValidaDadosImportacao();

            AtualizaCustoUnitario();
        }

        public void ImportaCustoUnitarioAmostras(string pFileName)
        {
            ExcelConnection = new Importacao.Excel();
            dsCustoUnitario = null;

            ValidaLayoutAmostraImportacao(pFileName);
            ValidaDadosImportacao();

            AtualizaCustoUnitarioAmostra();
            AtualizaProdutoPai();
        }

        private void AtualizaProdutoPai()
        {
            OnImportData(this, new ImportDataEventArgs("Atualizando relacionamento de amostras com o produto final..."));

            Produto Produto = new Produto();
            var ProdutoSQL = Produto.GetLista().ToDictionary(d => d.CodigoProduto);

            var Produtos = from p in dsCustoUnitario.Tables[0].AsEnumerable()
                           join db in ProdutoSQL.Values
                           on p.Field<string>("Produto").Trim().PadLeft(9, '0') equals db.CodigoProduto
                           select new TB_PRODUTO
                           {
                               ID_PRODUTO = db.IdProduto,
                               CODIGO_PRODUTO = db.CodigoProduto,
                               NOME_PRODUTO = db.NomeProduto,
                               ID_CENTRO_CUSTO = db.IdCentroCusto,
                               ID_PRODUTO_PAI = ProdutoSQL.ContainsKey(p.Field<string>("Amostra").Trim().PadLeft(9, '0')) == true ? ProdutoSQL[p.Field<string>("Amostra").Trim().PadLeft(9, '0')].IdProduto : (int?)null,
                               INDICA_ATIVO = db.IndicaAtivo,
                               CODIGO_PRODUTO_SEMI_ACABADO = db.CodigoProdutoSemiAcabado,
                               ID_PRODUTO_GLOBAL = db.IdProdutoGlobal,
                               ID_PRODUTO_TIPO = db.IdProdutoTipo,
                               ID_UNIDADE_NEGOCIO = db.IdUnidadeNegocio,
                               ID_PRODUTO_FAMILIA = db.IdProdutoFamilia
                           };

            int i = 0;
            foreach (var item in Produtos)
            {
                Produto.Update(item);
                i++;
                OnImportData(this, new ImportDataEventArgs("Atualizando relacionamento de amostras...", i, Produtos.Count()));
            }
        }

        private void AtualizaCustoUnitarioAmostra()
        {
            CustoUnitario Custo = new CustoUnitario();
            OnImportData(this, new ImportDataEventArgs("Importando informações de Custo Unitário..."));

            ProdutoGlobal Global = new ProdutoGlobal();
            var IdProdutoGlobal = Global.GetItens().ToDictionary(d => d.CodigoProdutoGlobal);

            // Coluna A, H
            List<CustoUnitario.Lista> CustoUnitarioExcel = (from p in dsCustoUnitario.Tables[0].AsEnumerable()
                                                            join j in IdProdutoGlobal.Select(s => s.Value)
                                                            on p.Field<string>("Amostra") equals j.CodigoProdutoGlobal
                                                            where j.IdProduto != 0
                                                            select new CustoUnitario.Lista
                                                            {
                                                                IdProduto = j.IdProduto,
                                                                ValorCustoUnitario = Convert.ToInt32(p.Field<object>("Performance"))
                                                            }).Distinct().ToList();

            List<CustoUnitario.Lista> CustoUnitarioSQL = (from p in Custo.GetAll()
                                                          select new CustoUnitario.Lista
                                                          {
                                                              IdProduto = p.ID_PRODUTO,
                                                              IdProdutoCustoUnitario = p.ID_PRODUTO_CUSTO_UNITARIO,
                                                              ValorCustoUnitario = p.VALOR_CUSTO_UNITARIO
                                                          }).ToList();

            // Compara os arquivos do Excel com os existentes no Banco de Dados
            var Novos = (from p in CustoUnitarioExcel
                         join db in CustoUnitarioSQL
                         on p.IdProduto equals db.IdProduto
                         into custoUnitario
                         from db in custoUnitario.DefaultIfEmpty()
                         where db == null
                         select new CustoUnitario.Lista
                         {
                             IdProduto = p.IdProduto,
                             ValorCustoUnitario = p.ValorCustoUnitario
                         }).ToList();

            int i = 0;
            if (Novos != null && Novos.Count() > 0)
            {
                foreach (var item in Novos)
                {
                    Custo.Add(new TB_PRODUTO_CUSTO_UNITARIO() { ID_PRODUTO = item.IdProduto, VALOR_CUSTO_UNITARIO = item.ValorCustoUnitario });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Incluindo Custo Unitário...", i, Novos.Count()));
                }
            }

            List<CustoUnitario.Lista> Alterados = (from p in CustoUnitarioExcel
                                                   join db in CustoUnitarioSQL
                                                   on p.IdProduto equals db.IdProduto
                                                   where p.IdProduto != db.IdProduto
                                                   select new CustoUnitario.Lista
                                                   {
                                                       IdProduto = db.IdProduto,
                                                       IdProdutoCustoUnitario = p.IdProdutoCustoUnitario,
                                                       ValorCustoUnitario = p.ValorCustoUnitario
                                                   }).ToList();
            i = 0;
            if (Alterados != null && Alterados.Count() > 0)
            {
                foreach (var item in Alterados)
                {
                    Custo.Update(new TB_PRODUTO_CUSTO_UNITARIO()
                    {
                        ID_PRODUTO = item.IdProduto,
                        ID_PRODUTO_CUSTO_UNITARIO = item.IdProdutoCustoUnitario,
                        VALOR_CUSTO_UNITARIO = item.ValorCustoUnitario
                    });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Alterando Custo Unitário...", i, Alterados.Count()));
                }
            }

            if (Novos != null && Novos.Count() == 0 && Alterados != null && Alterados.Count() == 0)
                OnImportData(this, new ImportDataEventArgs("Os Custos Unitários já estão importados. Não foram realizadas importações."));
            else
                OnImportData(this, new ImportDataEventArgs("Importação efetuada com sucesso."));
        }

        private void ValidaLayoutAmostraImportacao(string pFileName)
        {
            // Acessa o documento Excel e efetua a consulta na planilha "Dados"
            try { dsCustoUnitario = ExcelConnection.ExecuteRecordset(pFileName, "SELECT * FROM [Dados$]"); }
            catch { throw new Exception("A planilha não possui a aba \"Dados\"."); }

            if (dsCustoUnitario != null && dsCustoUnitario.Tables != null && dsCustoUnitario.Tables.Count > 0 && dsCustoUnitario.Tables[0].Rows.Count > 0)
            {
                try { var aux = dsCustoUnitario.Tables[0].Rows[0].Field<object>("Amostra"); }
                catch { throw new Exception("A coluna (\"Amostra\") não foi encontrada no arquivo."); }

                try { var aux = dsCustoUnitario.Tables[0].Rows[0].Field<object>("Produto"); }
                catch { throw new Exception("A coluna (\"Produto\") não foi encontrada no arquivo."); }

                try { var aux = dsCustoUnitario.Tables[0].Rows[0].Field<object>("Performance"); }
                catch { throw new Exception("A coluna (\"Performance\") não foi encontrada no arquivo."); }
            }
        }

        private void AtualizaCustoUnitario()
        {
            CustoUnitario Custo = new CustoUnitario();
            OnImportData(this, new ImportDataEventArgs("Importando informações de Custo Unitário..."));

            ProdutoGlobal Global = new ProdutoGlobal();
            var IdProdutoGlobal = Global.GetItens().ToDictionary(d => d.CodigoProdutoGlobal);

            // Coluna A, H
            List<CustoUnitario.Lista> CustoUnitarioExcel = (from p in dsCustoUnitario.Tables[0].AsEnumerable()
                                                            join j in IdProdutoGlobal.Select(s => s.Value)
                                                            on p.Field<string>("Code") equals j.CodigoProdutoGlobal
                                                            where j.IdProduto != 0
                                                            select new CustoUnitario.Lista
                                                             {
                                                                 IdProduto = j.IdProduto,
                                                                 ValorCustoUnitario = Convert.ToInt32(p.Field<object>("Performance"))
                                                             }).Distinct().ToList();

            List<CustoUnitario.Lista> CustoUnitarioSQL = (from p in Custo.GetAll()
                                                          select new CustoUnitario.Lista
                                       {
                                           IdProduto = p.ID_PRODUTO,
                                           IdProdutoCustoUnitario = p.ID_PRODUTO_CUSTO_UNITARIO,
                                           ValorCustoUnitario = p.VALOR_CUSTO_UNITARIO
                                       }).ToList();

            // Compara os arquivos do Excel com os existentes no Banco de Dados
            var Novos = (from p in CustoUnitarioExcel
                         join db in CustoUnitarioSQL
                         on p.IdProduto equals db.IdProduto
                         into custoUnitario
                         from db in custoUnitario.DefaultIfEmpty()
                         where db == null
                         select new CustoUnitario.Lista
                         {
                             IdProduto = p.IdProduto,
                             ValorCustoUnitario = p.ValorCustoUnitario
                         }).ToList();

            int i = 0;
            if (Novos != null && Novos.Count() > 0)
            {
                foreach (var item in Novos)
                {
                    Custo.Add(new TB_PRODUTO_CUSTO_UNITARIO() { ID_PRODUTO = item.IdProduto, VALOR_CUSTO_UNITARIO = item.ValorCustoUnitario });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Incluindo Custo Unitário...", i, Novos.Count()));
                }
            }

            List<CustoUnitario.Lista> Alterados = (from p in CustoUnitarioExcel
                                                   join db in CustoUnitarioSQL
                                                   on p.IdProduto equals db.IdProduto
                                                   where p.IdProduto != db.IdProduto
                                                   select new CustoUnitario.Lista
                                     {
                                         IdProduto = db.IdProduto,
                                         IdProdutoCustoUnitario = p.IdProdutoCustoUnitario,
                                         ValorCustoUnitario = p.ValorCustoUnitario
                                     }).ToList();
            i = 0;
            if (Alterados != null && Alterados.Count() > 0)
            {
                foreach (var item in Alterados)
                {
                    Custo.Update(new TB_PRODUTO_CUSTO_UNITARIO()
                    {
                        ID_PRODUTO = item.IdProduto,
                        ID_PRODUTO_CUSTO_UNITARIO = item.IdProdutoCustoUnitario,
                        VALOR_CUSTO_UNITARIO = item.ValorCustoUnitario
                    });
                    i++;
                    OnImportData(this, new ImportDataEventArgs("Alterando Custo Unitário...", i, Alterados.Count()));
                }
            }

            if (Novos != null && Novos.Count() == 0 && Alterados != null && Alterados.Count() == 0)
                OnImportData(this, new ImportDataEventArgs("Os Custos Unitários já estão importados. Não foram realizadas importações."));
            else
                OnImportData(this, new ImportDataEventArgs("Importação efetuada com sucesso."));
        }

        private void ValidaLayoutImportacao(string pFileName)
        {
            // Acessa o documento Excel e efetua a consulta na planilha "Dados"
            try{dsCustoUnitario = ExcelConnection.ExecuteRecordset(pFileName, "SELECT * FROM [Dados$]");}
            catch{throw new Exception("A planilha não possui a aba \"Dados\".");}

            if (dsCustoUnitario != null && dsCustoUnitario.Tables != null && dsCustoUnitario.Tables.Count > 0 && dsCustoUnitario.Tables[0].Rows.Count > 0)
            {
                try{var aux = dsCustoUnitario.Tables[0].Rows[0].Field<object>("Code");}
                catch{throw new Exception("A coluna (\"Code\") não foi encontrada no arquivo.");}

                try{var aux = dsCustoUnitario.Tables[0].Rows[0].Field<object>("Performance");}
                catch{throw new Exception("A coluna (\"Performance\") não foi encontrada no arquivo.");}
            }
        }

        private void ValidaDadosImportacao()
        {
            if (dsCustoUnitario != null && dsCustoUnitario.Tables != null && dsCustoUnitario.Tables.Count > 0 && dsCustoUnitario.Tables[0].Rows.Count > 0)
            {
                var CustoUnitario = (from p in dsCustoUnitario.Tables[0].AsEnumerable()
                                     where (p.Field<object>("Code") != null) && (p.Field<object>("Performance") == null)
                                     select p).ToList();

                if (CustoUnitario != null && CustoUnitario.Count() > 0)
                    throw new Exception("Existem custos em branco.\nPor favor, reveja a planilha e reinicie a importação.");
            }
        }

        #endregion Importa Custo Unitário

        #endregion Processos

        #region Classes

        /// <summary>
        /// Lista com as propriedades da classe
        /// </summary>
        public class Lista
        {
            public int IdProdutoCustoUnitario { get; set; }
            public int IdProduto { get; set; }
            public string CodigoProduto { get; set; }
            public decimal ValorCustoUnitario { get; set; }
        }

        #endregion Classes

        public event ImportDataEventHandler OnImportData;
    }

    public class CustoUnitarioBLO<T> : IBLO<T> where T : TB_PRODUTO_CUSTO_UNITARIO
    {
        /// <summary>
        /// CustoUnitarioDAO
        /// </summary>
        private CustoUnitarioDAO<T> _base = new CustoUnitarioDAO<T>();

        /// <summary>
        /// Adiciona uma entidade no sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        /// <returns>Entidade</returns>
        public T Add(T pEntity)
        {
            Validate(pEntity);
            _base.Add(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// Salva uma entidade do sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        /// <returns>Entidade</returns>
        public T Save(T pEntity)
        {
            Validate(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// Deleta uma entidade do sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        public void Delete(T pEntity)
        {
            _base.Delete(pEntity);
            _base.Save(pEntity);
        }

        /// <summary>
        /// Atualiza uma entidade do sistema
        /// </summary>
        /// <param name="pEntity">pEntity</param>
        /// <returns>Entidade</returns>
        public T Update(T pEntity)
        {
            Validate(pEntity);
            _base.Update(pEntity);
            return _base.Save(pEntity);
        }

        /// <summary>
        /// Recupera uma entidade do sistema
        /// </summary>
        /// <returns>Lista</returns>
        public IQueryable<T> GetAll()
        {
            return _base.GetAll();
        }

        /// <summary>
        /// Valida a consistência da Entidade antes de 'comitar' a transação
        /// </summary>
        /// <param name="pEntity">Entidade</param>
        public void Validate(T pEntity)
        {

        }
    }
}